
// Version: 2.1.1

// 内部变量
biBridgeId = null;
biEntryName = "unknown";
biIsChinese = false;
biIsOnline = false;
biDataPath = null;
biMtNames = null;
biNativeVersions = null;
biBusVersions = null;
biVideoVersions = null;
biDeviceVersions = null;
biProcessorVersions = null;
biFileVersions = null;
biSystemStatus = null;
biPythonTemplates = null; // Template-ID {String} -> Name {String}
biPreferPRCWeb = false;
biAppID = null;
biBufferRange = null; // [lower date, lower time, upper date, upper time]
biSampleBuffer = {}; // Full-protocol {String} -> Session {Date} -> Samples {Array<BIGeneralSample>}
biLocalVars = {}; // Key {String} -> Value {String}

// 内部函数
biSendBridgeMessage = (funcId, params) => {
    let comps = [];
    comps.push("BIBridgeMessage");
    comps.push(biBridgeId);
    comps.push(funcId);
    for (let i = 0; i < params.length; i++) {
        comps.push(params[i]);
    }
    window.top.postMessage(comps);
};

biInitBridgeClient = (bridgeId) => {
    biBridgeId = bridgeId;
    window.onmessage = (event) => {
        if (event.data == null || event.data instanceof Array == false) return;
        if (event.data.length < 3 || event.data[0] != "BIBridgeMessage") return;
        if (event.data[1] != biBridgeId) return;
        let funcId = event.data[2];
        let params = event.data.slice(3);
        switch (funcId) {
            case "SystemState":
                biUpdateSystemState(params);
                break;
            case "InitArgs":
                biLocalVars["System.ShowHelp"] = params[2] ? "yes" : "no";
                biOnInit(params[0]);
                biOnInitEx(params[0], params[1]);
                break;
            case "StartSession":
                biOnStartSession();
                break;
            case "StopSession":
                biOnStopSession();
                break;
            case "Interest":
                biOnSetInterest(params[0], params[1]);
                break;
            case "ResetSampleBuffer":
                biResetSampleBuffer();
                break;
            case "GeneralSample":
                biInputGeneralSample(params);
                break;
            case "Confirm":
                biConfirmCallback(params);
                break;
            case "AudioDriversInfo":
                biQueryAudioDriversInfoCallback(params);
                break;
            case "BusDevicesInfo":
                biQueryBusDevicesInfoCallback(params);
                break;
            case "BusMessageInfo":
                biQueryBusMessageInfoCallback(params);
                break;
            case "BusProtocolFileChannel":
                biQueryBusProtocolFileChannelCallback(params);
                break;
            case "BusProtocolInfo":
                biQueryBusProtocolInfoCallback(params);
                break;
            case "ChannelNames":
                biQueryChannelNamesCallback(params);
                break;
            case "GenerationList":
                biQueryGenerationListCallback(params);
                break;
            case "GlobalParameter":
                biQueryGlobalParameterCallback(params);
                break;
            case "GlobalVariable":
                biQueryGlobalVariableCallback(params);
                break;
            case "PythonModuleConfig":
                biQueryPythonModuleConfigCallback(params);
                break;
            case "PythonTemplateInfo":
                biQueryPythonTemplateInfoCallback(params);
                break;
            case "SessionList":
                biQuerySessionListCallback(params);
                break;
            case "SignalInfo":
                biQuerySignalInfoCallback(params);
                break;
            case "SignalsInBusMessage":
                biQuerySignalsInBusMessageCallback(params);
                break;
            case "VideoDevicesInfo":
                biQueryVideoDevicesInfoCallback(params);
                break;
            case "VideoJpeg":
                biQueryVideoJpegCallback(params);
                break;
            case "RunStandaloneTaskResult":
                biRunStandaloneTaskCallback(params);
                break;
            case "BusMessage":
                biSelectBusMessageCallback(params);
                break;
            case "BusMessages":
                biSelectBusMessagesCallback(params);
                break;
            case "BusProtocols":
                biSelectBusProtocolsCallback(params);
                break;
            case "Signal":
                biSelectSignalCallback(params);
                break;
            case "Signals":
                biSelectSignalsCallback(params);
                break;
        }
    };
    biSendBridgeMessage("ClientInitialized", []);
};

biUpdateSystemState = function (stateParams) {

    biEntryName = stateParams[0];
    biIsChinese = stateParams[1];
    biIsOnline = stateParams[2];
    biDataPath = stateParams[3];
    biMtNames = stateParams[4];
    biNativeVersions = stateParams[5];
    biBusVersions = stateParams[6];
    biVideoVersions = stateParams[7];
    biDeviceVersions = stateParams[8];
    biProcessorVersions = stateParams[9];
    biFileVersions = stateParams[10];
    biPythonTemplates = stateParams[12];
    biPreferPRCWeb = stateParams[13];
    biAppID = stateParams[14];
    biBufferRange = stateParams[15];

    biSystemStatus = {};
    for (let key in stateParams[11]) {
        let text = stateParams[11][key];
        let statusID = BISystemStatus[key];
        if (statusID != null) biSystemStatus[statusID] = text == null ? "" : text;
    }

    if (biBufferRange == null) {
        biSampleBuffer = {};
    }
    else {
        var targetSession = biBufferRange[0];
        var targetTime = biBufferRange[1];

        for (protocol in biSampleBuffer) {
            var sessionToRemove = [];
            var protocolSampleBuffer = biSampleBuffer[protocol];
            for (sessionString in protocolSampleBuffer) {
                var session = new Date(sessionString);
                if (session.getTime() < targetSession.getTime()) {
                    sessionToRemove.push(sessionString);
                } else if (session.getTime() == targetSession.getTime()) {
                    var sessionSamples = protocolSampleBuffer[sessionString];
                    var removeCount = 0;
                    for (; removeCount < sessionSamples.length; removeCount++) {
                        if (sessionSamples[removeCount].time >= targetTime) {
                            break;
                        }
                    }
                    if (removeCount > 0) {
                        sessionSamples.splice(0, removeCount);
                    }
                }
            }
            for (var i = 0; i < sessionToRemove.length; i++) {
                delete protocolSampleBuffer[sessionToRemove[i]];
            }
        }
    }
}

biResetSampleBuffer = function () {
    biSampleBuffer = {};
}

biInputGeneralSample = function (params) {

    let fullProtocol = params[0];
    let protocol = params[1];
    let channel = params[2];
    let session = params[3];
    let time = params[4];
    let values = params[5];

    if (!(fullProtocol in biSampleBuffer)) {
        biSampleBuffer[fullProtocol] = {};
    }
    var protocolSampleBuffer = biSampleBuffer[fullProtocol];
    if (!(session in protocolSampleBuffer)) {
        protocolSampleBuffer[session] = [];
    }
    var sessionSampleBuffer = protocolSampleBuffer[session];
    var newSample = new BIGeneralSample(protocol, channel, session, time, values);
    sessionSampleBuffer.push(newSample);
}

biSearchSample = function (samples, middle, left, right, interestTime) {
    if (left == middle) {
        return [middle, right];
    } else if (right == middle) {
        return [left, middle];
    } else if (samples[middle].time > interestTime) {
        return biSearchSample(samples, Math.floor((left + middle) / 2), left, middle, interestTime);
    } else {
        return biSearchSample(samples, Math.floor((right + middle) / 2), middle, right, interestTime);
    }
}

biBoolean = function (input, defaultBoolean) {
    if (input == null) {
        return defaultBoolean ? 1 : 0;
    } else {
        return input ? 1 : 0;
    }
}

biUnsupported = (funcName) => {
    biPrint(funcName + " is unsupported by appox.js.");
};

// 替换bi-common.js的功能函数
biUnimplemented = (funcName) => {
    biPrint(funcName + " unimplemented. The appox.js of later versions needed.");
};

biDeprecated = (funcName) => {
    biPrint(funcName + " is deprecated.");
};

biAddWindow = (windowTypeID, config, newWorkspaceIfNeeded) => {
    biUnsupported("biAddWindow");
};

biAlert = (msg, title) => {
    if (msg == null || msg == "") {
        biPrint("biAlert: msg invalid.");
        return;
    }
    biSendBridgeMessage("Alert", [msg.toString(), title == null ? "" : title.toString()]);
};

biCloseChildDialog = (result) => {
    biUnsupported("biCloseChildDialog");
};

biConfigDataEncryption = () => {
    biSendBridgeMessage("ConfigDataEncryption", []);
};

biConfirm = (key, msg, title) => {
    if (msg == null || msg == "") {
        biPrint("biConfirm: msg invalid.");
        return;
    }
    biSendBridgeMessage("Confirm", [key, msg.toString(), title == null ? "" : title.toString()]);
};

biConfirmCallback = (params) => {
    let key = params[0];
    let confirmed = params[1];
    biOnResultOfConfirm(key, confirmed == null ? true : confirmed);
}

biDirectoryDelete = (path, toRecycleBin) => {
    biUnsupported("biDirectoryDelete");
};

biDirectoryMake = (path) => {
    biUnsupported("biDirectoryMake");
};

biFileDelete = (path, toRecycleBin) => {
    biUnsupported("biFileDelete");
};

biGetAppID = () => {
    return biAppID;
};

biGetBufferRangeX = () => {
    if (biBufferRange != null) {
        return new BIBufferRangeX(biBufferRange[0], biBufferRange[1], biBufferRange[2], biBufferRange[3]);
    }
    else return null;
};

biGetDataPath = () => {
    return biDataPath;
};

biGetGeneralSamplePair = (protocol, channel, session, time, maxGap) => {
    if (protocol == null || protocol == "") {
        biPrint("biGetGeneralSamplePair: protocol invalid.");
        return null;
    } else if (session == null) {
        biPrint("biGetGeneralSamplePair: session invalid.");
        return null;
    } else if (time == null || time <= 0) {
        biPrint("biGetGeneralSamplePair: time invalid.");
        return null;
    } else if (maxGap == null || maxGap <= 0) {
        biPrint("biGetGeneralSamplePair: maxGap invalid.");
        return null;
    }
    
    let fullProtocol = protocol + (channel == null ? "" : ("@" + channel));
    if (!(fullProtocol in biSampleBuffer)) {
        return null;
    }
    let protocolSampleBuffer = biSampleBuffer[fullProtocol];
    if (!(session in protocolSampleBuffer)) {
        return null;
    }

    let result = new BIGeneralSamplePair();

    let samples = protocolSampleBuffer[session];
    if (samples.length == 0) {
        return null;
    } else if (samples.length == 1) {
        if (Math.abs(time - samples[0].time) > maxGap) {
            return null;
        } else {
            result.sample1 = samples[0];
            result.sample2 = samples[1];
            result.weight1 = 1.0;
            result.weight2 = 0.0;
            return result;
        }
    }

    if (time >= samples[samples.length - 1].time) {
        if (time > samples[samples.length - 1].time + maxGap) {
            return null;
        } else {
            result.sample1 = samples[samples.length - 1];
            result.sample2 = samples[samples.length - 1];
            result.weight1 = 1.0;
            result.weight2 = 0.0;
            return result;
        }
    }

    if (time <= samples[0].time) {
        if (time < samples[0].time - maxGap) {
            return null;
        } else {
            result.sample1 = samples[0];
            result.sample2 = samples[0];
            result.weight1 = 1.0;
            result.weight2 = 0.0;
            return result;
        }
    }
    
    let range = biSearchSample(samples, Math.floor(samples.length / 2), 0, samples.length - 1, time);
    if (range[0] == range[1]) {
        let targetSample = samples[range[0]];
        if (Math.abs(targetSample.time - time) > maxGap) {
            return null;
        } else {
            result.sample1 = targetSample;
            result.sample2 = targetSample;
            result.weight1 = 1.0;
            result.weight2 = 0.0;
            return result;
        }
    }

    let sample1 = samples[range[0]];
    let sample2 = samples[range[1]];
    let time1 = sample1.time;
    let time2 = sample2.time;
    let time1Gap = Math.abs(time1 - time);
    let time2Gap = Math.abs(time2 - time);

    if (time2 <= time1 && time1Gap < maxGap) {
        result.sample1 = sample1;
        result.sample2 = sample1;
        result.weight1 = 1.0;
        result.weight2 = 0.0;
        return result;
    } else if (time1Gap < maxGap && time2Gap < maxGap) {
        result.sample1 = sample1;
        result.sample2 = sample2;
        let delta = time2 - time1;
        result.weight1 = time2Gap / delta;
        result.weight2 = time1Gap / delta;
        return result;
    } else if (time1Gap < maxGap && time2Gap >= maxGap) {
        result.sample1 = sample1;
        result.sample2 = sample1;
        result.weight1 = 1.0;
        result.weight2 = 0.0;
        return result;
    } else if (time1Gap >= maxGap && time2Gap < maxGap) {
        result.sample1 = sample2;
        result.sample2 = sample2;
        result.weight1 = 1.0;
        result.weight2 = 0.0;
        return result;
    } else {
        return null;
    }
};

biGetLanguage = () => {
    return biIsChinese ? 2/* BILanguage.Chinese */ : 1/* BILanguage.English */;
};

biGetLocalVariable = (id, defaultValue) => {
    if (id == null || id == "") {
        biPrint("biGetLocalVariable: id invalid.");
        return null;
    }
    if (defaultValue == null) {
        defaultValue = "null";
    }
    if (!(id in biLocalVars)) {
        return defaultValue;
    }
    return biLocalVars[id];
};

biGetManualTriggerNames = () => {
    return biMtNames;
};

biGetNativeModuleVersions = (category) => {
    if (category == null) {
        category = 0;
    }
    if (category == 1) { // BINativeCategory.Native
        return biNativeVersions;
    } else if (category == 2) { // BINativeCategory.Bus
        return biBusVersions;
    } else if (category == 3) { // BINativeCategory.Video
        return biVideoVersions;
    } else if (category == 4) { // BINativeCategory.Device
        return biDeviceVersions;
    } else if (category == 5) { // BINativeCategory.Processor
        return biProcessorVersions;
    } else if (category == 6) { // BINativeCategory.File
        return biFileVersions;
    } else {
        biPrint("biGetNativeModuleVersions: category invalid.");
        return null;
    }
};

biGetPlatform = () => {
    return 3; // BIPlatform.Browser
};

biGetPythonTemplateNames = () => {
    return biPythonTemplates;
};

biGetRunningMode = () => {
    return biIsOnline ? 2/* BIRunningMode.Online */ : 1/* BIRunningMode.Offline */;
};

biGetSubDataPaths = () => {
    biUnsupported("biGetSubDataPaths");
    return null;
};

biGetSystemStatus = (status) => {
    if (status == null || status == 0) {
        biPrint("biGetSystemStatus: status invalid.");
        return null;
    }
    return biSystemStatus[status];
};

biGetUIBackend = () => {
    return 1; // BIUIBackend.Web
};

biGetWebEngine = () => {
    return 1; // BIWebEngine.Any
};

biIsPRCWebPreferred = () => {
    return biPreferPRCWeb;
};

biListenGeneralSample = (protocols, channel) => {
    if (protocols == null || protocols.length == 0) {
        biPrint("biListenGeneralSample: protocols invalid.");
        return;
    }
    biSendBridgeMessage("ListenGeneralSample", [protocols, channel]);
};

biLog = (msg, level) => {
    if (msg == null || msg == "") {
        biPrint("biLog: msg invalid.");
        return;
    } else if (level == null) {
        biPrint("biLog: level invalid.");
        return;
    }
    biSendBridgeMessage("Log", [msg.toString(), level]);
};

biOpenChildDialog = (htmlName, title, size, config) => {
    biUnsupported("biOpenChildDialog");
};

biOpenDialog = (dialogTypeID, config) => {
    biUnsupported("biOpenDialog");
};

biOpenHTMLDialog = (entryName, config) => {
    biUnsupported("biOpenHTMLDialog");
};

biPrint = (msg) => {
    if (msg == null || msg == "") {
        return;
    }
    biSendBridgeMessage("Print", ["[" + biEntryName + "] " + msg.toString()]);
};

biQueryAudioDriversInfo = () => {
    biSendBridgeMessage("QueryAudioDriversInfo", []);
};

biQueryAudioDriversInfoCallback = (params) => {
    let driversInfo = [];
    for (let i = 0; i < params.length; i++) {
        let driverInfo = params[i];
        let recordDevices = [];
        let replayDevices = [];
        for (let j = 0; j < driverInfo.recDevs.length; j++) {
            recordDevices.push(new BIAudioDeviceInfo(driverInfo.recDevs[j].id, driverInfo.recDevs[j].name));
        }
        for (let j = 0; j < driverInfo.repDevs.length; j++) {
            replayDevices.push(new BIAudioDeviceInfo(driverInfo.repDevs[j].id, driverInfo.repDevs[j].name));
        }
        driversInfo.push(new BIAudioDriverInfo(driverInfo.id, driverInfo.name, recordDevices, replayDevices));
    }
    biOnQueriedAudioDriversInfo(driversInfo);
}

biQueryBusDevicesInfo = () => {
    biSendBridgeMessage("QueryBusDevicesInfo", []);
};

biQueryBusDevicesInfoCallback = (params) => {
    let devicesInfo = [];
    for (let i = 0; i < params.length; i++) {
        let deviceInfo = params[i];
        devicesInfo.push(new BIBusDeviceInfo(deviceInfo.type, deviceInfo.serial, deviceInfo.index, deviceInfo.description, deviceInfo.supported));
    }
    biOnQueriedBusDevicesInfo(devicesInfo);
}

biQueryBusMessageInfo = (key, busMessageID) => {
    if (busMessageID == null || busMessageID == "") {
        biPrint("biQueryBusMessageInfo: busMessageID invalid.");
        return;
    }
    biSendBridgeMessage("QueryBusMessageInfo", [key, busMessageID]);
};

biQueryBusMessageInfoCallback = (params) => {
    let key = params[0];
    let busMessageID = params[1];
    let protocolFileID = params[2];
    let localID = params[3];
    let messageName = params[4];
    if (busMessageID == null) {
        biOnQueriedBusMessageInfo(key, null);
    } else {
        biOnQueriedBusMessageInfo(key, new BIBusMessageInfo(busMessageID, protocolFileID, localID, messageName));
    }
}

biQueryBusProtocolFileChannel = (busProtocolFileName) => {
    if (busProtocolFileName == null || busProtocolFileName == "") {
        biPrint("biQueryBusProtocolFileChannel: busProtocolFileName invalid.");
        return;
    }
    biSendBridgeMessage("QueryBusProtocolFileChannel", [busProtocolFileName]);
};

biQueryBusProtocolFileChannelCallback = (params) => {
    let busProtocolFileName = params[0];
    let busChannel = params[1];
    biOnQueriedBusProtocolFileChannel(busProtocolFileName, busChannel);
}

biQueryBusProtocolInfo = (key, busProtocolID) => {
    if (busProtocolID == null || busProtocolID.fileName == null || busProtocolID.md5 == null) {
        biPrint("biQueryBusProtocolInfo: busProtocolID invalid");
        return;
    }
    biSendBridgeMessage("QueryBusProtocolInfo", [key, busProtocolID.fileName, busProtocolID.md5]);
};

biQueryBusProtocolInfoCallback = (params) => {
    let key = params[0];
    let busProtocolIdFileName = params[1];
    let busProtocolIdMD5 = params[2];
    let state = params[3];
    let channel = params[4];
    if (state == null) {
        biOnQueriedBusProtocolInfo(key, null);
    } else {
        biOnQueriedBusProtocolInfo(key, new BIBusProtocolInfo(new BIBusProtocolID(busProtocolIdFileName, busProtocolIdMD5), BIBusProtocolState[state], channel, null));
    }
}

biQueryChannelNames = (key, protocol, channelCount) => {
    if (protocol == null || protocol == "") {
        biPrint("biQueryChannelNames: protocol invalid.");
        return;
    }
    biSendBridgeMessage("QueryChannelNames", [key, protocol, channelCount]);
};

biQueryChannelNamesCallback = (params) => {
    let key = params[0];
    let names = params[1];
    biOnQueriedChannelNames(key, names);
}

biQueryDirectoryExist = (path) => {
    biUnsupported("biQueryDirectoryExist");
};

biQueryDirsInDirectory = (path) => {
    biUnsupported("biQueryDirsInDirectory");
};

biQueryFileExist = (path) => {
    biUnsupported("biQueryFileExist");
};

biQueryFilesInDirectory = (path) => {
    biUnsupported("biQueryFilesInDirectory");
};

biQueryFileText = (path) => {
    biUnsupported("biQueryFileText");
};

biQueryGenerationList = () => {
    biSendBridgeMessage("QueryGenerationList", []);
};

biQueryGenerationListCallback = (params) => {
    let list = params[0];
    biOnQueriedGenerationList(list);
}

biQueryGenerationPath = (session, generationID) => {
    biUnsupported("biQueryGenerationPath");
};

biQueryGlobalParameter = (id, defaultValue) => {
    if (id == null || id == "") {
        biPrint("biQueryGlobalParameter: id invalid.");
        return;
    }
    if (defaultValue == null) {
        defaultValue = "null";
    }
    biSendBridgeMessage("QueryGlobalParameter", [id, defaultValue]);
};

biQueryGlobalParameterCallback = (params) => {
    let id = params[0];
    let value = params[1];
    biOnQueriedGlobalParameter(id, value);
}

biQueryGlobalPath = (id) => {
    biUnsupported("biQueryGlobalPath");
};

biQueryGlobalVariable = (id, defaultValue) => {
    if (id == null || id == "") {
        biPrint("biQueryGlobalVariable: id invalid.");
        return;
    }
    if (defaultValue == null) {
        defaultValue = "null";
    }
    biSendBridgeMessage("QueryGlobalVariable", [id, defaultValue]);
};

biQueryGlobalVariableCallback = (params) => {
    let id = params[0];
    let value = params[1];
    biOnQueriedGlobalVariable(id, value);
}

biQueryPythonModuleConfig = (templateID, idPostfix) => {
    if (templateID == null || templateID == "") {
        biPrint("biQueryPythonModuleConfig: templateID invalid.");
        return;
    } else if (idPostfix == null || idPostfix == "") {
        biPrint("biQueryPythonModuleConfig: idPostfix invalid.");
        return;
    }
    biSendBridgeMessage("QueryPythonModuleConfig", [templateID, idPostfix]);
};

biQueryPythonModuleConfigCallback = (params) => {
    let templateID = params[0];
    let idPostfix = params[1];
    let config = params[2];
    if (config == null) {
        biOnQueriedPythonModuleConfig(templateID, idPostfix, null);
    } else {
        biOnQueriedPythonModuleConfig(templateID, idPostfix, new BIPythonModuleConfig(config.enable, config.name, config.freq, config.params));
    }
}

biQueryPythonTemplateInfo = (templateID) => {
    if (templateID == null || templateID == "") {
        biPrint("biQueryPythonTemplateInfo: templateID invalid.");
        return;
    }
    biSendBridgeMessage("QueryPythonTemplateInfo", [templateID]);
};

biQueryPythonTemplateInfoCallback = (params) => {
    let templateID = params[0];
    let infoXmlBase64 = params[1];
    biOnQueriedPythonTemplateInfo(templateID, infoXmlBase64);
}

biQuerySessionList = (filtered) => {
    biSendBridgeMessage("QuerySessionList", [biBoolean(filtered, false)]);
};

biQuerySessionListCallback = (params) => {
    let list = params[0];
    let filtered = params[1];
    biOnQueriedSessionList(list, filtered);
}

biQuerySessionPath = (session) => {
    biUnsupported("biQuerySessionPath");
};

biQuerySignalInfo = (key, signalID) => {
    if (signalID == null || signalID == "") {
        biPrint("biQuerySignalInfo: signalID invalid.");
        return;
    }
    biSendBridgeMessage("QuerySignalInfo", [key, signalID]);
};

biQuerySignalInfoCallback = (params) => {
    let key = params[0];
    let signalInfo = params[1];
    biOnQueriedSignalInfo(key, new BISignalInfo(signalInfo[0], signalInfo[1], signalInfo[2], signalInfo[3], signalInfo[4]));
}

biQuerySignalsInBusMessage = (key, busMessageID) => {
    if (busMessageID == null || busMessageID == "") {
        biPrint("biQuerySignalsInBusMessage: busMessageID invalid.");
        return;
    }
    biSendBridgeMessage("QuerySignalsInBusMessage", [key, busMessageID]);
};

biQuerySignalsInBusMessageCallback = (params) => {
    let key = params[0];
    let signalIDList = params[1];
    biOnQueriedSignalsInBusMessage(key, signalIDList);
}

biQueryVideoDevicesInfo = () => {
    biSendBridgeMessage("QueryVideoDevicesInfo", []);
};

biQueryVideoDevicesInfoCallback = (params) => {
    let devicesInfo = [];
    let devicesInfoX = [];
    for (let i = 0; i < params.length; i++) {
        let deviceInfo = params[i];
        let inputModes = [];
        for (let j = 0; j < deviceInfo.inModes.length; j++) {
            inputModes.push(new BIVideoInputMode(BIVideoCodec[deviceInfo.inModes[j].codec], new BISize(deviceInfo.inModes[j].width, deviceInfo.inModes[j].height)));
        }
        let outputModes = [];
        for (let j = 0; j < deviceInfo.outModes.length; j++) {
            outputModes.push(new BIVideoOutputMode(BIVideoCodec[deviceInfo.outModes[j].codec], new BISize(deviceInfo.outModes[j].width, deviceInfo.outModes[j].height)));
        }
        devicesInfo.push(new BIVideoDeviceInfo(deviceInfo.type, deviceInfo.id, deviceInfo.info, inputModes));
        devicesInfoX.push(new BIVideoDeviceInfoX(deviceInfo.type, deviceInfo.id, deviceInfo.info, inputModes, outputModes));
    }
    biOnQueriedVideoDevicesInfo(devicesInfo);
    biOnQueriedVideoDevicesInfoX(devicesInfoX);
}

biQueryVideoJpeg = async (channel, session, time, maxGap) => {
    if (channel == null || channel < 0 || channel >= 24) {
        biPrint("biQueryVideoJpeg: channel invalid.");
        return;
    } else if (time != null && time <= 0) {
        biPrint("biQueryVideoJpeg: time invalid.");
        return;
    } else if (maxGap != null && maxGap <= 0) {
        biPrint("biQueryVideoJpeg: maxGap invalid.");
        return;
    }
    biSendBridgeMessage("QueryVideoJpeg", [channel, session, time, maxGap]);
};

biQueryVideoJpegCallback = (params) => {
    let jpegBase64 = params[0];
    let channel = params[1];
    let sessionDate = params[2];
    let time = params[3];
    biOnQueriedVideoJpeg(jpegBase64, channel, sessionDate, time);
}

biRunStandaloneTask = (key, taskTypeID, config) => {
    if (taskTypeID == null || taskTypeID == "") {
        biPrint("biRunStandaloneTask: taskTypeID invalid.");
        return;
    }
    biSendBridgeMessage("RunStandaloneTask", [key, taskTypeID, config]);
};

biRunStandaloneTaskCallback = (params) => {
    let key = params[0];
    let taskResult = BITaskResult[params[1]];
    let returnValue = params[2];
    biOnResultOfStandaloneTask(key, taskResult, returnValue);
}

biSelectBusMessage = (key, originID) => {
    biSendBridgeMessage("SelectBusMessage", [key, originID]);
};

biSelectBusMessageCallback = (params) => {
    let key = params[0];
    let result = params[1];
    let protocolFileID = params[2];
    let localID = params[3];
    let messageName = params[4];
    if (result == null) {
        biOnSelectedBusMessage(key, null);
    } else {
        biOnSelectedBusMessage(key, new BIBusMessageInfo(result, protocolFileID, localID, messageName));
    }
};

biSelectBusMessages = (key, selected, availableCount) => {
    biSendBridgeMessage("SelectBusMessages", [key, selected, availableCount]);
};

biSelectBusMessagesCallback = (params) => {
    let key = params[0];
    let result = params[1];
    biOnSelectedBusMessages(key, result);
}

biSelectBusProtocols = (key, selected) => {
    biSendBridgeMessage("SelectBusProtocols", [key, selected == null ? [] : selected.map(id => {return {name: id.fileName, md5: id.md5}; })]);
};

biSelectBusProtocolsCallback = (params) => {
    let key = params[0];
    let result = params[1];
    if (result == null) {
        biOnSelectedBusProtocols(key, null);
    } else {
        biOnSelectedBusProtocols(key, result.map(id => new BIBusProtocolID(id.name, id.md5)));
    }
}

biSelectPath = (key, type, filter) => {
    biUnsupported("biSelectPath");
};

biSelectSignal = (key, originValueID, withSignBit, originSignBitID, withScale, originScale, unit) => {
    biSendBridgeMessage("SelectSignal", [key, originValueID, biBoolean(withSignBit, false), originSignBitID, biBoolean(withScale, false), originScale, unit]);
};

biSelectSignalCallback = (params) => {
    let key = params[0];
    let valueSignalInfo = params[1];
    let signBitSignalInfo = params[2];
    let scale = params[3];
    biOnSelectedSignal(key, 
        valueSignalInfo == null ? null : new BISignalInfo(valueSignalInfo[0], valueSignalInfo[1], valueSignalInfo[2], valueSignalInfo[3], valueSignalInfo[4]),
        signBitSignalInfo == null ? null : new BISignalInfo(signBitSignalInfo[0], signBitSignalInfo[1], signBitSignalInfo[2], signBitSignalInfo[3], signBitSignalInfo[4]),
        scale);
}

biSelectSignals = (key, selected, availableCount) => {
    biSendBridgeMessage("SelectSignals", [key, selected, availableCount]);
};

biSelectSignalsCallback = (params) => {
    let key = params[0];
    let result = params[1];
    biOnSelectedSignals(key, result);
}

biSetGlobalParameter = (id, value) => {
    if (id == null || id == "") {
        biPrint("biSetGlobalParameter: id invalid.");
        return;
    } else if (value == null) {
        return;
    }
    biSendBridgeMessage("SetGlobalParameter", [id, value]);
};

biSetGlobalPath = (id, paths) => {
    biUnsupported("biSetGlobalPath");
};

biSetGlobalVariable = (id, value) => {
    if (id == null || id == "") {
        biPrint("biSetGlobalVariable: id invalid.");
        return;
    } else if (value == null) {
        return;
    }
    biSendBridgeMessage("SetGlobalVariable", [id, value]);
};

biSetLocalVariable = (id, value) => {
    if (id == null || id == "") {
        biPrint("biSetLocalVariable: id invalid.");
        return;
    } else if (value == null) {
        return;
    }
    biLocalVars[id] = value;
};

biSetModuleConfig = (moduleClassID, config) => {
    if (moduleClassID == null || moduleClassID == "") {
        biPrint("biSetModuleConfig: moduleClassID invalid.");
        return;
    }
    biSendBridgeMessage("SetModuleConfig", [moduleClassID, config]);
};

biSetPythonModuleConfig = (templateID, idPostfix, config) => {
    if (templateID == null || templateID == "") {
        biPrint("biSetPythonModuleConfig: templateID invalid.");
        return;
    } else if (idPostfix == null || idPostfix == "") {
        biPrint("biSetPythonModuleConfig: idPostfix invalid.");
        return;
    }
    biSendBridgeMessage("SetPythonModuleConfig", [templateID, idPostfix, config]);
};

biSetViewConfig = (config) => {
    biSendBridgeMessage("ViewConfig", [config]);
};

biStartProcess = (target) => {
    biUnsupported("biStartProcess");
};

biWriteFileText = (path, text) => {
    biUnsupported("biWriteFileText");
};